package yl.hs.bmipfull.service.system.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import yl.hs.bmipfull.annotations.DBTable;
import yl.hs.bmipfull.daos.system.*;
import yl.hs.bmipfull.pojo.system.*;
import yl.hs.bmipfull.service.system.IRoleService;
import yl.hs.bmipfull.utils.*;
import yl.hs.bmipfull.utils.datastate.ActionType;
import yl.hs.bmipfull.viewmodels.FilterFieldModel;
import yl.hs.bmipfull.viewmodels.PageQuery;
import yl.hs.bmipfull.viewmodels.PageResult;
import yl.hs.bmipfull.viewmodels.QueryFilter;
import yl.hs.bmipfull.viewmodels.system.MenuView;
import yl.hs.bmipfull.viewmodels.system.ViewShareTag;

import java.util.*;
import java.util.stream.Collectors;

/**
 * RoleServiceImpl
 *
 * @author 谢景广
 * @category 用户角色管理
 * @date 2021-05-10
 */
@Service
public class RoleServiceImpl extends SystemBaseService implements IRoleService {

    @Autowired
    IIconLocationDao dao;
    @Autowired
    IAppSettingDao settingDao;
    @Override
    public RoleInfoEntity findRoleBySn(String sn) {
        return roleDao.findBySN(sn);
    }
    @Transactional
    public Integer saveRoleInfo(Map<String, Object> roleModel, String sn) throws Exception {
        int result = 0;
        var user = HsApp.getCurrent();
        if (user != null) {
            roleModel.put("companySN", user.getCompanySN());
            roleModel.put("organizationCode", user.getOrganizationCode());
        }
        roleModel.putIfAbsent("state", 0);
        RoleInfoEntity role = null;
        if (Tl.isEmpty(sn)) {
            var newSn = queryNewSN(RoleInfoEntity.class);
            role = new RoleInfoEntity();
            Tl.getChangeLog(role, roleModel);
            role.setSn(newSn);
            result = roleDao.insertNew(role);
            writeLog(ActionType.CREATE, newSn, "", "", RoleInfoEntity.class);
        } else {
            role = roleDao.findBySN(sn);
            var backup = role.toJson();
            var changeLog = Tl.getChangeLog(role, roleModel);
            role.setPYFieldValue();
            result = roleDao.update(role);
            if (result > 0) {
                tagDao.updateTag(role.getSn(), role.getRoleName());
                writeLog(ActionType.UPDATE, sn, changeLog, backup, RoleInfoEntity.class);
            }
        }

        return result;
    }

    @Override
    public PageResult<RoleInfoEntity> queryPage(PageQuery query, int rowNum, int page) {
        Map<String, Object> queryParam = new HashMap<String, Object>();
        if (query.getQuery().containsKey("key")) {
            queryParam.put("key", String.format("%%%s%%", query.getQuery().get("key")));
        }

        PageResult<RoleInfoEntity> result = new PageResult(rowNum);
        result.setRecords(roleDao.queryCountByCondition(queryParam));
        result.setPage(page);
        queryParam.put("querySkip", (page - 1) * rowNum);
        queryParam.put("pageSize", rowNum);
        query.checkSortSides(RoleInfoEntity.class);
        queryParam.put("sortSide", String.join(",", query.getSortSides()));
        result.setData(roleDao.queryListByCondition(queryParam));
        return result;
    }

    @Override
    public List<RoleInfoEntity> queryAll() {
        var condition = String.format(" state>=%d", DataState.CREATED.code);
        return roleDao.queryByCondition(condition);
    }

    @Transactional
    public Integer deleteRoleBySN(String[] sn) {
        var result = roleDao.changeRoleState(DataState.DELETED.code, sn);
        if (result > 0) {
            tagDao.deleteTagByTagSN(sn);
            writeLog(ActionType.DELETE, sn, RoleInfoEntity.class);
        }
        return result;
    }

    @Override
    public List<Map<String,Object>> findUserMenus(String userSN, boolean isAdmin) throws ClassNotFoundException {
        var set="";
        if (!isAdmin){
            set=HsApp.getSystemSet(CK.SysVersion);
        }
        var allMenuList = menuInfoDao.queryAllMenus(set);
        var allBtnList = buttonDao.queryAllButtons();
        var userAuthorizes = new ArrayList<String>();
        if (isAdmin) {
            userAuthorizes.addAll(allMenuList.stream().map(MenuInfoEntity::getSn).collect(Collectors.toList()));
            userAuthorizes.addAll(allBtnList.stream().map(MenuButtonEntity::getSn).collect(Collectors.toList()));
        } else {
            var userRoles = tagDao.queryByMainSn(userSN, Tl.getDBMapCode(RoleInfoEntity.class));
            var userRoleSnArray = userRoles.stream().map(CoreDataTagEntity::getTagSN).collect(Collectors.toList());

            if (userRoleSnArray!= null && userRoleSnArray.size() > 0) {
                var authorizes = authorizeDao.queryByOwnerSN(userRoleSnArray.toArray(new String[userRoleSnArray.size()]));
                userAuthorizes.addAll(authorizes.stream().map(AuthorizeInfoEntity::getFeatureSN).distinct().collect(Collectors.toList()));
            }
        }
        List<Map<String,Object>> menuObjList = new ArrayList<>();
        var pojoList = HsApp.getPojoClass();
        //var filterList = searchFilterDao.queryUserSearchFilters(userSN);
        var views = searchViewDao.queryQueryViews(userSN);
        var filters = searchFilterDao.queryFiltersByViewSNList(views.stream().map(t->t.getSn()).toList());
        var userViews = svuDao.queryUserViews(userSN);
        if(userAuthorizes.size()>0) {
           var list = allMenuList.stream().filter(t -> t.getLayer() == 0).toList();
            for (int i = 0; i < list.size(); i++) {
                var t = list.get(i);
                if(userAuthorizes.contains(t.getSn())) {
                    var map = generateMenu(allBtnList, t, userAuthorizes);
                    map.put("children", getChild(allMenuList, allBtnList, t.getSn(), userAuthorizes,pojoList,filters,views,userViews));
                    menuObjList.add(map);
                }
            }
        }
        return menuObjList;
    }

    @Override
    public List<String> queryRolePermission(String roleSN) {
        var permission = authorizeDao.queryByOwnerSN(new String[]{roleSN});
        return permission.stream().map(AuthorizeInfoEntity::getFeatureSN).collect(Collectors.toList());
    }

    @Transactional
    public Integer saveRolePermission(String roleSN, List<String> permissions) {
        var set="";
        var currentUser=HsApp.getCurrent();
        if (currentUser.getAccount().equals(settingDao.getByCode("supperAdministrator").getParamValue())){
            set=settingDao.getByCode("SystemVersion").getParamValue();
        }
        var role = findRoleBySn(roleSN);
        var menuList = menuInfoDao.queryAllMenus(set);
        var menuBtnList = buttonDao.queryAllButtons();
        var menuTypeCode = Tl.getDBMapCode(MenuInfoEntity.class);
        var menuBtnCode = Tl.getDBMapCode(MenuButtonEntity.class);
        var oldPermissions = authorizeDao.queryByOwnerSN(new String[]{roleSN});
        var oldPermissionIds = oldPermissions.stream().map(AuthorizeInfoEntity::getFeatureSN).collect(Collectors.toList());
        authorizeDao.deleteByOwnerSN(roleSN);
        var count = 0;
        for (var itemId : permissions) {
            var type = "";
            if (menuList.stream().anyMatch(t -> t.getSn().equals(itemId))) {
                type = menuTypeCode;
            }
            if (menuBtnList.stream().anyMatch(t -> t.getSn().equals(itemId))) {
                type = menuBtnCode;
            }
            if (!Tl.isEmpty(type)) {
                var authorize = new AuthorizeInfoEntity();
                authorize.setOwnerSN(role.getSn());
                var t = role.getClass().getName().split("\\.");
                authorize.setOwnerType(t[t.length - 1]);
                authorize.setFeatureSN(itemId);
                authorize.setFeatureType(type);
                count += authorizeDao.insertNew(authorize);
            }
        }
        if (count > 0) {
            var user = HsApp.getCurrent();
            var userName = user != null ? user.getUserName() : HsApp.getClient();
            writeLog(ActionType.UPDATE, role.getSn(), String.format("%s修改了角色%s的授权：%s", userName, role.getRoleName(),
                    getChangeLog(permissions, oldPermissionIds, menuBtnList, menuList)), Tl.toJson(oldPermissions), AuthorizeInfoEntity.class);
        }
        return count;
    }

    @Override
    public Map<String, Object> getAllRoleMap() {
        var obj = new HashMap<String,Object>();
        var allRole = queryAll();
        if(allRole.size()>0) {
            for (var i = 0; i < allRole.size(); i++) {
                var role = allRole.get(i);
                if (!obj.containsKey(role.getSn())) {
                    obj.put(role.getSn(), role.getRoleName());
                }
            }
        }
        return obj;
    }

    private List<Object> getChild(List<MenuInfoEntity> allList, List<MenuButtonEntity> allBtnList, String parentSN, List<String> userAuthorizes,
                                  List<Class<?>> pojoList,List<SearchFilterEntity> filterList,List<SearchViewEntity> views,
                                  List<SearchViewUpdateEntity> userViews) throws ClassNotFoundException {
        var menuList = allList.stream().filter(t -> t.getParentSN() != null && t.getParentSN().equals(parentSN)).collect(Collectors.toList());
        var data = new ArrayList<Object>();
        var user = HsApp.getCurrent();
        ClassNotFoundException exception = null;
        for (int i = 0; i < menuList.size(); i++) {
            var t = menuList.get(i);
            try {
                if (userAuthorizes.contains(t.getSn())) {
                    var map = generateMenu(allBtnList, t, userAuthorizes);
                    map.put("children", getChild(allList, allBtnList, t.getSn(), userAuthorizes,pojoList,filterList,views,userViews));
                    List<FilterFieldModel> fields = new ArrayList<>();
                    List<MenuView> viewList = new ArrayList<MenuView>();
                    var menuViews = views.stream().filter(f->f.getMenuSN().equals(t.getSn())).toList();
                    if(menuViews.size()>0){
                        for (int j = 0; j < menuViews.size(); j++) {
                            var mv = new MenuView();
                            var mvItem = menuViews.get(j);
                            var mvFilters = filterList.stream().filter(s->s.getViewSN().equals(mvItem.getSn())).toList();
                            mv.setSn(mvItem.getSn());
                            mv.setTitle(mvItem.getTitle());
                            mv.setShareType(mvItem.getShareType());
                            var dsFields = new ArrayList<String>();
                            if(!Tl.isEmpty(mvItem.getDisplayFields())){
                                var fieldArray = Arrays.stream(mvItem.getDisplayFields().split(",")).toList();
                                dsFields.addAll(fieldArray);
                            }
                            List<Map<String,String>> tags = new ArrayList<Map<String,String>>();
                            tags.addAll(Tl.toObject(mvItem.getTags(),tags.getClass()));
                            mv.setEdit(tags.stream().filter(g->user.getUserSN().equals(g.get("sn")) && ViewShareTag.OWNER.equals(g.get("type"))).count()>0);
                            mv.setFields(dsFields);
                            mv.setFilters(getFilters(mvFilters));
                            long last = 0;
                            var suv =userViews.stream().filter(u->mvItem.getSn().equals( u.getViewSN())).count();
                            if(suv>0){
                                var uv = userViews.stream().filter(u->mvItem.getSn().equals( u.getViewSN())).findFirst();
                                if(!uv.isEmpty()){
                                    last = uv.get().getLastUseView();
                                }
                            }
                            mv.setLast(last);
                            viewList.add(mv);
                        }
                    }
                    Class<?> cls = null;
                    if (t.getPojoName() != null && !"".equals(t.getPojoName())) {
                        if(t.getPojoName().indexOf("hs.bmipfull")>0){
                            cls = Class.forName(t.getPojoName());
                        }
                        else if (pojoList.size() > 0) {
                            var clsResult = pojoList.stream().filter(s -> s.getName().endsWith(t.getPojoName())).findFirst();
                            if (!clsResult.isEmpty()) {
                                cls = clsResult.get();
                                t.setPojoName(cls.getName());
                                menuInfoDao.update(t);
                            }
                        }
                        if(cls!=null) {
                            fields.addAll(SearchFilterEngine.getFilterFields(cls));
                            map.put("tableName", cls.getAnnotation(DBTable.class).mapCode());
                        }
                    }
                    if(cls!=null) {
                        map.put("tableName", cls.getAnnotation(DBTable.class).mapCode());
                    }
                    map.put("fields", fields);
                    if(viewList.size()>0) {
                        viewList = viewList.stream().sorted(Comparator.comparing(MenuView::getLast).reversed()).collect(Collectors.toList());
                    }
                    map.put("views", viewList);
                    var menuSN = map.get("sn").toString();
                    var iconLocationEntity = dao.find(user.getUserSN(),menuSN);
                    if(iconLocationEntity!=null){
                        map.put("x",iconLocationEntity.getX());
                        map.put("y",iconLocationEntity.getY());
                        map.put("w",iconLocationEntity.getW());
                        map.put("h",iconLocationEntity.getH());
                        map.put("i",iconLocationEntity.getI());
                        map.put("visible",iconLocationEntity.isVisible());
                    };
                    data.add(map);
                }
            }
            catch (ClassNotFoundException ee){
                exception = ee;
                break;
            }
        }
        if(exception !=null){
            throw  exception;
        }
        return data;
    }
    private List<Map<String,Object>> getFilters(List<SearchFilterEntity> filters){
        List<Map<String,Object>> list = new ArrayList<>();
        for (int i = 0; i < filters.size(); i++) {
            var item = new HashMap<String,Object>();
            var s = filters.get(i);
            item.put("field",s.getField());
            item.put("operator",s.getFilterType());
            item.put("searchVal",s.getSearchValue());
            list.add(item);
        }
        return  list
                ;
    }
    private Map<String, Object> generateMenu(List<MenuButtonEntity> allBtnList, MenuInfoEntity t, List<String> userAuthorizes) {
        var map = new HashMap<String, Object>();
        if(userAuthorizes.contains(t.getSn())) {
            map.put("pageAddress", t.getPageAddress());
            map.put("fullName", t.getFullName());
            map.put("component", t.getComponent());
            map.put("menuIcon", t.getMenuIcon());
            map.put("target", t.getTarget());
            map.put("dataAddress", t.getDataAddress());
            map.put("printAddress", t.getPrintAddress());
            map.put("sn", t.getSn());
            map.put("keepAlive", t.isKeepAlive());
            map.put("state", t.getState());
            map.put("refreshtime",t.getRefreshtime());
            map.put("dataCode",t.getMainDataCode());
            map.put("remark", t.getRemark());
            map.put("appPageFun",t.getAppPageFun());
            map.put("versions",t.getVersions());
            var buttons = getMenuButtons(allBtnList, t.getSn(), userAuthorizes);
            if (buttons.size() > 0) {
                map.put("btnPermissions", String.join(",", buttons));
            }
        }
        return map;
    }

    private List<String> getMenuButtons(List<MenuButtonEntity> allButtons, String menuSN, List<String> userAuthorizes) {
        var buttons = new ArrayList<String>();
        allButtons.stream().filter(t -> t.getMenuSN() != null && t.getMenuSN().equals(menuSN)).forEach(t -> {
            if (userAuthorizes.contains(t.getSn())) {
                buttons.add(t.getEncode());
            }
        });
        return buttons;
    }

    private String getChangeLog(List<String> newItems, List<String> oldList, List<MenuButtonEntity> allButtons,
                                List<MenuInfoEntity> allMenus) {
        var oldItems = oldList.toArray(new String[oldList.size()]);
        var newPermission = newItems.toArray(new String[newItems.size()]);
        var addItems = Tl.except(newPermission, oldItems);
        var subItems = Tl.except(oldItems, newPermission);
        var addFeathers = getMenusByFeathers(allMenus, allButtons, addItems);
        var subFeathers = getMenusByFeathers(allMenus, allButtons, subItems);
        StringBuilder changeDetailsSB = new StringBuilder();
        if (subFeathers.size() > 0) {
            changeDetailsSB.append("移除了功能：" + String.join("、\r\n", subFeathers));
        }
        if (addFeathers.size() > 0) {
            changeDetailsSB.append("新增了功能：" + String.join("、\r\n", addFeathers));
        }
        return changeDetailsSB.toString();
    }

    private List<String> getMenusByFeathers(List<MenuInfoEntity> allMenus, List<MenuButtonEntity> allButtons,
                                            List<String> itemSNs) {
        var menus = allMenus.stream().filter(t -> itemSNs.contains(t.getSn())).collect(Collectors.toList());
        var buttons = allButtons.stream().filter(t -> itemSNs.contains(t.getSn())).collect(Collectors.toList());
        var feathers = new ArrayList<String>();
        for (var menuItem : menus) {
            if (menus.stream().filter(t -> t.getParentSN() != null && t.getParentSN().equals(menuItem.getSn()))
                    .count() == 0) {
                var actions = buttons.stream()
                        .filter(t -> t.getMenuSN() != null && t.getMenuSN().equals(menuItem.getSn())).map(MenuButtonEntity::getFullName).collect(Collectors.toList());
                feathers.add(String.format("%s【%s】", menuItem.getFullName(), String.join(",", actions)));
            }
        }
        return feathers;
    }

    @Autowired
    ICoreDataTagDao tagDao;

    @Autowired
    IRoleInfoDao roleDao;

    @Autowired
    IAuthorizeDao authorizeDao;

    @Autowired
    IMenuInfoDao menuInfoDao;

    @Autowired
    IMenuButtonDao buttonDao;

    @Autowired
    ISearchFilterDao searchFilterDao;

    @Autowired
    ISearchViewDao searchViewDao;

    @Autowired
    ISearchViewUpdateDao svuDao;
}
